texture: Add GdkMemoryTexture
authorBenjamin Otte <otte@redhat.com>
Tue, 6 Mar 2018 02:41:13 +0000 (03:41 +0100)
committerBenjamin Otte <otte@redhat.com>
Wed, 7 Mar 2018 15:17:15 +0000 (16:17 +0100)
GdkMemoryTexture is a texture implementation for holding data in memory
(read: GBytes). You specify the GdkMemoryFormat that data is in and off
you go.

Renderers can use this to add uploads in various different formats and
don't need to fallback to GDK doing the conersion on the CPU.

Supported formats can be extended if we need new ones, for now I just
added the relevant ones for Cairo and GdkPixbuf.

The constructor is also private still, because I'm not sure we want to
export GdkMemoryFormat.
Wrappers that do from_cairo_surface() and for_pixbuf() do exist though.

gdk/gdkmemorytexture.c [new file with mode: 0644]
gdk/gdkmemorytextureprivate.h [new file with mode: 0644]
gdk/gdktexture.c
gdk/meson.build

diff --git a/gdk/gdkmemorytexture.c b/gdk/gdkmemorytexture.c
new file mode 100644 (file)
index 0000000..99c2e4a
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "config.h"
+
+#include "gdkmemorytextureprivate.h"
+
+struct _GdkMemoryTexture
+{
+  GdkTexture parent_instance;
+
+  GdkMemoryFormat format;
+
+  GBytes *bytes;
+  gsize stride;
+};
+
+struct _GdkMemoryTextureClass
+{
+  GdkTextureClass parent_class;
+};
+
+G_DEFINE_TYPE (GdkMemoryTexture, gdk_memory_texture, GDK_TYPE_TEXTURE)
+
+static void
+gdk_memory_texture_dispose (GObject *object)
+{
+  GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (object);
+
+  g_clear_pointer (&self->bytes, g_bytes_unref);
+
+  G_OBJECT_CLASS (gdk_memory_texture_parent_class)->dispose (object);
+}
+
+static void
+gdk_memory_texture_download (GdkTexture *texture,
+                             guchar     *data,
+                             gsize       stride)
+{
+  GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (texture);
+
+  gdk_memory_convert (data, stride,
+                      GDK_MEMORY_CAIRO_FORMAT_ARGB32,
+                      g_bytes_get_data (self->bytes, NULL), self->stride,
+                      self->format,
+                      texture->width, texture->height);
+}
+
+static void
+gdk_memory_texture_class_init (GdkMemoryTextureClass *klass)
+{
+  GdkTextureClass *texture_class = GDK_TEXTURE_CLASS (klass);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  texture_class->download = gdk_memory_texture_download;
+  gobject_class->dispose = gdk_memory_texture_dispose;
+}
+
+static void
+gdk_memory_texture_init (GdkMemoryTexture *self)
+{
+}
+
+GdkTexture *
+gdk_memory_texture_new (int              width,
+                        int              height,
+                        GdkMemoryFormat  format,
+                        GBytes          *bytes,
+                        gsize            stride)
+{
+  GdkMemoryTexture *self;
+
+  self = g_object_new (GDK_TYPE_MEMORY_TEXTURE,
+                       "width", width,
+                       "height", height,
+                       NULL);
+
+  self->format = format;
+  self->bytes = g_bytes_ref (bytes);
+  self->stride = stride;
+
+  return GDK_TEXTURE (self);
+}
+
+GdkMemoryFormat 
+gdk_memory_texture_get_format (GdkMemoryTexture *self)
+{
+  return self->format;
+}
+
+const guchar *
+gdk_memory_texture_get_data (GdkMemoryTexture *self)
+{
+  return g_bytes_get_data (self->bytes, NULL);
+}
+
+gsize
+gdk_memory_texture_get_stride (GdkMemoryTexture *self)
+{
+  return self->stride;
+}
+
+static void
+convert_memcpy (guchar       *dest_data,
+                gsize         dest_stride,
+                const guchar *src_data,
+                gsize         src_stride,
+                gsize         width,
+                gsize         height)
+{
+  gsize y;
+
+  for (y = 0; y < height; y++)
+    memcpy (dest_data + y * dest_stride, src_data + y * src_stride, 4 * width);
+}
+
+#define SWIZZLE(A,R,G,B) \
+static void \
+convert_swizzle ## A ## R ## G ## B (guchar       *dest_data, \
+                                     gsize         dest_stride, \
+                                     const guchar *src_data, \
+                                     gsize         src_stride, \
+                                     gsize         width, \
+                                     gsize         height) \
+{ \
+  gsize x, y; \
+\
+  for (y = 0; y < height; y++) \
+    { \
+      for (x = 0; x < width; x++) \
+        { \
+          dest_data[4 * x + A] = src_data[4 * x + 0]; \
+          dest_data[4 * x + R] = src_data[4 * x + 1]; \
+          dest_data[4 * x + G] = src_data[4 * x + 2]; \
+          dest_data[4 * x + B] = src_data[4 * x + 3]; \
+        } \
+\
+      dest_data += dest_stride; \
+      src_data += src_stride; \
+    } \
+}
+
+SWIZZLE(3,2,1,0)
+
+#define SWIZZLE_OPAQUE(A,R,G,B) \
+static void \
+convert_swizzle_opaque_## A ## R ## G ## B (guchar       *dest_data, \
+                                            gsize         dest_stride, \
+                                            const guchar *src_data, \
+                                            gsize         src_stride, \
+                                            gsize         width, \
+                                            gsize         height) \
+{ \
+  gsize x, y; \
+\
+  for (y = 0; y < height; y++) \
+    { \
+      for (x = 0; x < width; x++) \
+        { \
+          dest_data[4 * x + A] = 0xFF; \
+          dest_data[4 * x + R] = src_data[3 * x + 0]; \
+          dest_data[4 * x + G] = src_data[3 * x + 1]; \
+          dest_data[4 * x + B] = src_data[3 * x + 2]; \
+        } \
+\
+      dest_data += dest_stride; \
+      src_data += src_stride; \
+    } \
+}
+
+SWIZZLE_OPAQUE(3,2,1,0)
+SWIZZLE_OPAQUE(3,0,1,2)
+SWIZZLE_OPAQUE(0,1,2,3)
+SWIZZLE_OPAQUE(0,3,2,1)
+
+#define PREMULTIPLY(d,c,a) G_STMT_START { guint t = c * a + 0x80; d = ((t >> 8) + t) >> 8; } G_STMT_END
+#define SWIZZLE_PREMULTIPLY(A,R,G,B, A2,R2,G2,B2) \
+static void \
+convert_swizzle_premultiply_ ## A ## R ## G ## B ## _ ## A2 ## R2 ## G2 ## B2 \
+                                    (guchar       *dest_data, \
+                                     gsize         dest_stride, \
+                                     const guchar *src_data, \
+                                     gsize         src_stride, \
+                                     gsize         width, \
+                                     gsize         height) \
+{ \
+  gsize x, y; \
+\
+  for (y = 0; y < height; y++) \
+    { \
+      for (x = 0; x < width; x++) \
+        { \
+          dest_data[4 * x + A] = src_data[4 * x + A2]; \
+          PREMULTIPLY(dest_data[4 * x + R], src_data[4 * x + R2], src_data[4 * x + A2]); \
+          PREMULTIPLY(dest_data[4 * x + G], src_data[4 * x + G2], src_data[4 * x + A2]); \
+          PREMULTIPLY(dest_data[4 * x + B], src_data[4 * x + B2], src_data[4 * x + A2]); \
+        } \
+\
+      dest_data += dest_stride; \
+      src_data += src_stride; \
+    } \
+}
+
+SWIZZLE_PREMULTIPLY (3,2,1,0, 3,2,1,0)
+SWIZZLE_PREMULTIPLY (0,1,2,3, 3,2,1,0)
+SWIZZLE_PREMULTIPLY (3,2,1,0, 0,1,2,3)
+SWIZZLE_PREMULTIPLY (0,1,2,3, 0,1,2,3)
+SWIZZLE_PREMULTIPLY (3,2,1,0, 3,0,1,2)
+SWIZZLE_PREMULTIPLY (0,1,2,3, 3,0,1,2)
+SWIZZLE_PREMULTIPLY (3,2,1,0, 0,3,2,1)
+SWIZZLE_PREMULTIPLY (0,1,2,3, 0,3,2,1)
+
+typedef void (* ConversionFunc) (guchar       *dest_data,
+                                 gsize         dest_stride,
+                                 const guchar *src_data,
+                                 gsize         src_stride,
+                                 gsize         width,
+                                 gsize         height);
+
+static ConversionFunc converters[GDK_MEMORY_N_FORMATS][2] =
+{
+  { convert_memcpy, convert_swizzle3210 },
+  { convert_swizzle3210, convert_memcpy },
+  { convert_swizzle_premultiply_3210_3210, convert_swizzle_premultiply_0123_3210 },
+  { convert_swizzle_premultiply_3210_0123, convert_swizzle_premultiply_0123_0123 },
+  { convert_swizzle_premultiply_3210_3012, convert_swizzle_premultiply_0123_3012 },
+  { convert_swizzle_premultiply_3210_0321, convert_swizzle_premultiply_0123_0321 },
+  { convert_swizzle_opaque_3210, convert_swizzle_opaque_3012 },
+  { convert_swizzle_opaque_0123, convert_swizzle_opaque_0321 }
+};
+
+void
+gdk_memory_convert (guchar          *dest_data,
+                    gsize            dest_stride,
+                    GdkMemoryFormat  dest_format,
+                    const guchar    *src_data,
+                    gsize            src_stride,
+                    GdkMemoryFormat  src_format,
+                    gsize            width,
+                    gsize            height)
+{
+  g_assert (dest_format < 2);
+  g_assert (src_format < GDK_MEMORY_N_FORMATS);
+
+  converters[src_format][dest_format] (dest_data, dest_stride, src_data, src_stride, width, height);
+}
diff --git a/gdk/gdkmemorytextureprivate.h b/gdk/gdkmemorytextureprivate.h
new file mode 100644 (file)
index 0000000..540ec26
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#ifndef __GDK_MEMORY_TEXTURE_PRIVATE_H__
+#define __GDK_MEMORY_TEXTURE_PRIVATE_H__
+
+#include "gdktextureprivate.h"
+
+G_BEGIN_DECLS
+
+/*
+ * GdkMemoryFormat:
+ *
+ * #GdkMemroyFormat describes a format that bytes can have in memory.
+ *
+ * It describes formats by listing the contents of the memory passed to it.
+ * So GDK_MEMORY_A8R8G8B8 will be 8 bits of alpha, followed by 8 bites of each 
+ * blue, green and red. It is not endian-dependant, so CAIRO_FORMAT_ARGB32 is
+ * represented by 2 different GdkMemoryFormats.
+ * 
+ * Its naming is modelled after VkFormat (see
+ * https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VkFormat
+ * for details).
+ */
+typedef enum {
+  GDK_MEMORY_B8G8R8A8_PREMULTIPLIED,
+  GDK_MEMORY_A8R8G8B8_PREMULTIPLIED,
+  GDK_MEMORY_B8G8R8A8,
+  GDK_MEMORY_A8R8G8B8,
+  GDK_MEMORY_R8G8B8A8,
+  GDK_MEMORY_A8B8G8R8,
+  GDK_MEMORY_R8G8B8,
+  GDK_MEMORY_B8G8R8,
+
+  GDK_MEMORY_N_FORMATS
+} GdkMemoryFormat;
+
+#define GDK_MEMORY_GDK_PIXBUF_OPAQUE GDK_MEMORY_R8G8B8
+#define GDK_MEMORY_GDK_PIXBUF_ALPHA GDK_MEMORY_R8G8B8A8
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define GDK_MEMORY_CAIRO_FORMAT_ARGB32 GDK_MEMORY_B8G8R8A8_PREMULTIPLIED
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+#define GDK_MEMORY_CAIRO_FORMAT_ARGB32 GDK_MEMORY_A8R8G8B8_PREMULTIPLIED
+#else
+#error "Unknown byte order."
+#endif
+
+#define GDK_TYPE_MEMORY_TEXTURE (gdk_memory_texture_get_type ())
+
+G_DECLARE_FINAL_TYPE (GdkMemoryTexture, gdk_memory_texture, GDK, MEMORY_TEXTURE, GdkTexture)
+
+GdkTexture *            gdk_memory_texture_new              (int                width,
+                                                             int                height,
+                                                             GdkMemoryFormat    format,
+                                                             GBytes            *bytes,
+                                                             gsize              stride);
+
+GdkMemoryFormat         gdk_memory_texture_get_format       (GdkMemoryTexture  *self);
+const guchar *          gdk_memory_texture_get_data         (GdkMemoryTexture  *self);
+gsize                   gdk_memory_texture_get_stride       (GdkMemoryTexture  *self);
+
+void                    gdk_memory_convert                  (guchar            *dest_data,
+                                                             gsize              dest_stride,
+                                                             GdkMemoryFormat    dest_format,
+                                                             const guchar      *src_data,
+                                                             gsize              src_stride,
+                                                             GdkMemoryFormat    src_format,
+                                                             gsize              width,
+                                                             gsize              height);
+
+
+G_END_DECLS
+
+#endif /* __GDK_MEMORY_TEXTURE_PRIVATE_H__ */
index 8cfd0abc60935acddd6acd9909f05ed0a77899ad..616b0cbb8bb7a07d82d5f487144437eea13d5dab 100644 (file)
@@ -36,8 +36,8 @@
 
 #include "gdktextureprivate.h"
 
-#include "gdkcairo.h"
 #include "gdkinternals.h"
+#include "gdkmemorytextureprivate.h"
 
 /**
  * SECTION:gdktexture
@@ -211,82 +211,6 @@ gdk_texture_init (GdkTexture *self)
 {
 }
 
-/* GdkCairoTexture */
-
-#define GDK_TYPE_CAIRO_TEXTURE (gdk_cairo_texture_get_type ())
-
-G_DECLARE_FINAL_TYPE (GdkCairoTexture, gdk_cairo_texture, GDK, CAIRO_TEXTURE, GdkTexture)
-
-struct _GdkCairoTexture {
-  GdkTexture parent_instance;
-  cairo_surface_t *surface;
-};
-
-struct _GdkCairoTextureClass {
-  GdkTextureClass parent_class;
-};
-
-G_DEFINE_TYPE (GdkCairoTexture, gdk_cairo_texture, GDK_TYPE_TEXTURE)
-
-static void
-gdk_cairo_texture_finalize (GObject *object)
-{
-  GdkCairoTexture *self = GDK_CAIRO_TEXTURE (object);
-
-  cairo_surface_destroy (self->surface);
-
-  G_OBJECT_CLASS (gdk_cairo_texture_parent_class)->finalize (object);
-}
-
-static cairo_surface_t *
-gdk_cairo_texture_download_surface (GdkTexture *texture)
-{
-  GdkCairoTexture *self = GDK_CAIRO_TEXTURE (texture);
-
-  return cairo_surface_reference (self->surface);
-}
-
-static void
-gdk_cairo_texture_download (GdkTexture *texture,
-                            guchar     *data,
-                            gsize       stride)
-{
-  GdkCairoTexture *self = GDK_CAIRO_TEXTURE (texture);
-  cairo_surface_t *surface;
-  cairo_t *cr;
-
-  surface = cairo_image_surface_create_for_data (data,
-                                                 CAIRO_FORMAT_ARGB32,
-                                                 texture->width, texture->height,
-                                                 stride);
-  cr = cairo_create (surface);
-
-  cairo_set_source_surface (cr, self->surface, 0, 0);
-  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
-  cairo_paint (cr);
-
-  cairo_destroy (cr);
-  cairo_surface_finish (surface);
-  cairo_surface_destroy (surface);
-}
-
-static void
-gdk_cairo_texture_class_init (GdkCairoTextureClass *klass)
-{
-  GdkTextureClass *texture_class = GDK_TEXTURE_CLASS (klass);
-  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-
-  texture_class->download = gdk_cairo_texture_download;
-  texture_class->download_surface = gdk_cairo_texture_download_surface;
-
-  gobject_class->finalize = gdk_cairo_texture_finalize;
-}
-
-static void
-gdk_cairo_texture_init (GdkCairoTexture *self)
-{
-}
-
 /**
  * gdk_texture_new_for_data:
  * @data: (array): the pixel data
@@ -341,90 +265,28 @@ gdk_texture_new_for_data (const guchar *data,
 GdkTexture *
 gdk_texture_new_for_surface (cairo_surface_t *surface)
 {
-  GdkCairoTexture *texture;
+  GdkTexture *texture;
+  GBytes *bytes;
 
   g_return_val_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL);
   g_return_val_if_fail (cairo_image_surface_get_width (surface) > 0, NULL);
   g_return_val_if_fail (cairo_image_surface_get_height (surface) > 0, NULL);
 
-  texture = g_object_new (GDK_TYPE_CAIRO_TEXTURE,
-                          "width", cairo_image_surface_get_width (surface),
-                          "height", cairo_image_surface_get_height (surface),
-                          NULL);
+  bytes = g_bytes_new_with_free_func (cairo_image_surface_get_data (surface),
+                                      cairo_image_surface_get_height (surface)
+                                      * cairo_image_surface_get_stride (surface),
+                                      (GDestroyNotify) cairo_surface_destroy,
+                                      cairo_surface_reference (surface));
+  
+  texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface),
+                                    cairo_image_surface_get_height (surface),
+                                    GDK_MEMORY_CAIRO_FORMAT_ARGB32,
+                                    bytes,
+                                    cairo_image_surface_get_stride (surface));
 
-  texture->surface = cairo_surface_reference (surface);
+  g_bytes_unref (bytes);
 
-  return (GdkTexture *) texture;
-}
-
-/* GdkPixbufTexture */
-
-#define GDK_TYPE_PIXBUF_TEXTURE (gdk_pixbuf_texture_get_type ())
-
-G_DECLARE_FINAL_TYPE (GdkPixbufTexture, gdk_pixbuf_texture, GDK, PIXBUF_TEXTURE, GdkTexture)
-
-struct _GdkPixbufTexture {
-  GdkTexture parent_instance;
-
-  GdkPixbuf *pixbuf;
-};
-
-struct _GdkPixbufTextureClass {
-  GdkTextureClass parent_class;
-};
-
-G_DEFINE_TYPE (GdkPixbufTexture, gdk_pixbuf_texture, GDK_TYPE_TEXTURE)
-
-static void
-gdk_pixbuf_texture_finalize (GObject *object)
-{
-  GdkPixbufTexture *self = GDK_PIXBUF_TEXTURE (object);
-
-  g_object_unref (self->pixbuf);
-
-  G_OBJECT_CLASS (gdk_pixbuf_texture_parent_class)->finalize (object);
-}
-
-static void
-gdk_pixbuf_texture_download (GdkTexture *texture,
-                             guchar     *data,
-                             gsize       stride)
-{
-  GdkPixbufTexture *self = GDK_PIXBUF_TEXTURE (texture);
-  cairo_surface_t *surface;
-
-  surface = cairo_image_surface_create_for_data (data,
-                                                 CAIRO_FORMAT_ARGB32,
-                                                 texture->width, texture->height,
-                                                 stride);
-  gdk_cairo_surface_paint_pixbuf (surface, self->pixbuf);
-  cairo_surface_finish (surface);
-  cairo_surface_destroy (surface);
-}
-
-static cairo_surface_t *
-gdk_pixbuf_texture_download_surface (GdkTexture *texture)
-{
-  GdkPixbufTexture *self = GDK_PIXBUF_TEXTURE (texture);
-
-  return gdk_cairo_surface_create_from_pixbuf (self->pixbuf, 1, NULL);
-}
-
-static void
-gdk_pixbuf_texture_class_init (GdkPixbufTextureClass *klass)
-{
-  GdkTextureClass *texture_class = GDK_TEXTURE_CLASS (klass);
-  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-
-  texture_class->download = gdk_pixbuf_texture_download;
-  texture_class->download_surface = gdk_pixbuf_texture_download_surface;
-
-  gobject_class->finalize = gdk_pixbuf_texture_finalize;
-}
-
-static void
-gdk_pixbuf_texture_init (GdkPixbufTexture *self)
-{
+  return texture;
 }
 
 /**
@@ -438,18 +300,28 @@ gdk_pixbuf_texture_init (GdkPixbufTexture *self)
 GdkTexture *
 gdk_texture_new_for_pixbuf (GdkPixbuf *pixbuf)
 {
-  GdkPixbufTexture *self;
+  GdkTexture *texture;
+  GBytes *bytes;
 
   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
 
-  self = g_object_new (GDK_TYPE_PIXBUF_TEXTURE,
-                       "width", gdk_pixbuf_get_width (pixbuf),
-                       "height", gdk_pixbuf_get_height (pixbuf),
-                       NULL);
+  bytes = g_bytes_new_with_free_func (gdk_pixbuf_get_pixels (pixbuf),
+                                      gdk_pixbuf_get_height (pixbuf)
+                                      * gdk_pixbuf_get_rowstride (pixbuf),
+                                      g_object_unref,
+                                      g_object_ref (pixbuf));
+  
+  texture = gdk_memory_texture_new (gdk_pixbuf_get_width (pixbuf),
+                                    gdk_pixbuf_get_height (pixbuf),
+                                    gdk_pixbuf_get_has_alpha (pixbuf)
+                                    ? GDK_MEMORY_GDK_PIXBUF_ALPHA
+                                    : GDK_MEMORY_GDK_PIXBUF_OPAQUE,
+                                    bytes,
+                                    gdk_pixbuf_get_rowstride (pixbuf));
+
+  g_bytes_unref (bytes);
 
-  self->pixbuf = g_object_ref (pixbuf);
-
-  return GDK_TEXTURE (self);
+  return texture;
 }
 
 /**
index 760cad8b6414a245f89bf1cb21fe3c70d1dadf68..10d2c4e698521856fa5c1d771ee9dec3e35259a4 100644 (file)
@@ -27,6 +27,7 @@ gdk_public_sources = files([
   'gdkgltexture.c',
   'gdkkeys.c',
   'gdkkeyuni.c',
+  'gdkmemorytexture.c',
   'gdkmonitor.c',
   'gdkpango.c',
   'gdkpixbuf-drawable.c',